#include "widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
    mEditStart->setRange(0, INT_MAX);
    mEditStart->setValue(1);
    mEditGrayThr->setRange(0, 256);
    mEditGrayThr->setValue(40);
    mEditXSum->setRange(2, 100);
    mEditXSum->setValue(2);
    mEditYSum->setRange(2, 100);
    mEditYSum->setValue(2);
    mEditIndexSum->setRange(1, 1000);
    mEditIndexSum->setValue(1);

    connect(mBtnOK, SIGNAL(clicked()), this, SLOT(onBtnClicked()));

    QGridLayout *layEdit = new QGridLayout;
    layEdit->addWidget(mLabPath, 0, 0, 1, 1);
    layEdit->addWidget(mEditPath, 0, 1, 1, 4);
    layEdit->addWidget(mLabName, 1, 0, 1, 1);
    layEdit->addWidget(mEditName, 1, 1, 1, 4);
    layEdit->addWidget(mLabStart, 2, 0, 1, 1);
    layEdit->addWidget(mEditStart, 2, 1, 1, 4);
    layEdit->addWidget(mLabGrayThr, 3, 0, 1, 1);
    layEdit->addWidget(mEditGrayThr, 3, 1, 1, 4);
    layEdit->addWidget(mLabFnName, 4, 0, 1, 1);
    layEdit->addWidget(mEditFnName, 4, 1, 1, 4);
    layEdit->addWidget(mLabXSum, 5, 0, 1, 1);
    layEdit->addWidget(mEditXSum, 5, 1, 1, 4);
    layEdit->addWidget(mLabYSum, 6, 0, 1, 1);
    layEdit->addWidget(mEditYSum, 6, 1, 1, 4);
    layEdit->addWidget(mLabIndexSum, 7, 0, 1, 1);
    layEdit->addWidget(mEditIndexSum, 7, 1, 1, 4);

    QVBoxLayout *layMain = new QVBoxLayout;
    layMain->addLayout(layEdit);
    layMain->addWidget(mBtnOK, 0, Qt::AlignRight);
    layMain->addStretch();
    setLayout(layMain);
}

Widget::~Widget()
{

}

int Widget::toGray(QRgb rgb) {
    return (qRed(rgb) + qGreen(rgb) + qBlue(rgb)) / 3;
}
template<typename T> void Widget::safeDelete(T *&t) {
    if(t) {
        delete t;
        t = nullptr;
    }
}

#define FN_FORMATCP(arg1, cpVar) {                          \
    QString str = "unsigned char arrCp%1[LEN%2] = {";       \
    out << "    " << str.arg(arg1, strFrameCount) << "\n";  \
    out << "        ";                                      \
    int count = 0;                                          \
    bool hasPrev = false;                                   \
    for(const ChangedPart &cp : listChangedParts) {         \
        if(hasPrev) {                                       \
            out << ',';                                     \
            if(count > 20) {                                \
                out << "\n        ";                        \
                count = 0;                                  \
            } else out << ' ';                              \
        } else hasPrev = true;                              \
        out << cpVar;                                       \
        count++;                                            \
    }                                                       \
    out << "\n    };\n";                                    \
}
void Widget::onBtnClicked() {
    QString canonicalPath = QFileInfo(mEditPath->text()).canonicalFilePath();
    QDir dir(canonicalPath);
    QStringList list = dir.entryList(QDir::NoDotAndDotDot | QDir::Files);

    //清除原有的c文件
    for(const QString &fileName : list) {
        if(QFileInfo(fileName).suffix() == "c") {
            dir.remove(fileName);
        }
    }

    int xSum = mEditXSum->value();
    int ySum = mEditYSum->value();

    //写入base.c
    QFile baseFile(canonicalPath + "/base.c");
    if(!baseFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "错误", "写入 \"base.c\" 失败，处理终止");
        return;
    }
    QTextStream ts(&baseFile);
    ts << "#ifndef BASE_C\n";
    ts << "#define BASE_C\n";
    ts << "\n";
    ts << "#include <" + mEditFnName->text() + ".h>\n";
    ts << "\n";
    ts << "#define XSUM " + QString::number(xSum) + "\n";
    ts << "#define YSUM " + QString::number(ySum) + "\n";
    ts << "\n";
    ts << "unsigned char data[320][240];\n";
    ts << "\n";
    ts << "void runSingle(unsigned char *arrCpX, unsigned char *arrCpY, unsigned char *arrCpLen, int len) {\n";
    ts << "    for(int k = 0; k < len; k++) {\n";
    ts << "        short y = arrCpY[k];\n";
    ts << "        for(short i = 0; i < arrCpLen[k]; i++) {\n";
    ts << "            short x = arrCpX[k] + i;\n";
    ts << "            unsigned char isWhite = data[x][y] = !data[x][y];\n";
    ts << "            " + mEditFnName->text() + "(x * XSUM, y * YSUM, isWhite ? WHITE : BLACK);\n";
    ts << "        }\n";
    ts << "    }\n";
    ts << "}\n";
    ts << "\n";
    ts << "#endif";
    baseFile.close();

    //遍历所有图像
    bool arrIsWhite[320][240];
    memset(arrIsWhite, 0, sizeof(bool) * 320 * 240);
    struct ChangedPart
    {
        //ChangedPart() = default;
        ChangedPart(short x, short y, bool isWhite) : x(x), y(y), isWhite(isWhite), len(0) {}
        short x;
        short y;
        bool isWhite;
        uchar len;
    };
    QString fileName = mEditName->text();
    int thr = mEditGrayThr->value();
    int frameCount = 0;
    int index = mEditStart->value();
    int indexSum = mEditIndexSum->value();
    while(dir.exists(fileName.arg(index))) {
        QImage img;
        if(!img.load(canonicalPath + "/" + fileName.arg(index))) {
            QMessageBox::critical(this, "错误", QString("读取 \"%1\" 失败，处理终止").arg(fileName));
            return;
        }
        qDebug() << fileName.arg(index);

        int width = qMin(320, img.width());
        int height = qMin(240, img.height());

        QList<ChangedPart> listChangedParts;
        for(short j = 0; j < height; j += ySum) {
            ChangedPart *cp = nullptr;
            for(short i = 0; i < width; i += xSum) {
                bool isWhite = toGray(img.pixel(i, j)) >= thr;
                if(isWhite != arrIsWhite[i][j]) {
                    if(!cp)
                        cp = new ChangedPart(i, j, isWhite);
                    else if(isWhite != cp->isWhite) {
                        listChangedParts << *cp;
                        safeDelete(cp);
                        cp = new ChangedPart(i, j, isWhite);
                    }
                    if(cp->len == UCHAR_MAX) {
                        listChangedParts << *cp;
                        safeDelete(cp);
                        cp = new ChangedPart(i, j, isWhite);
                    }
                    arrIsWhite[i][j] = isWhite;
                    cp->len++;
                } else if(cp) {
                    listChangedParts << *cp;
                    safeDelete(cp);
                }
            }
            if(cp) {
                listChangedParts << *cp;
                safeDelete(cp);
            }
        }

        //写入c文件
        QString write = QString::number(frameCount) + ".c";
        QFile file(canonicalPath + "/" + write);
        if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
            QMessageBox::critical(this, "错误", QString("写入 \"\" 失败，处理终止").arg(write));
            return;
        }
        QString define = "_" + QString::number(frameCount);
        QString strFrameCount = QString::number(frameCount);
        QString strSize = QString::number(listChangedParts.size());
        QTextStream out(&file);
        out << "#ifndef " + define + "\n";
        out << "#define " + define + "\n";
        out << "\n";
        out << "#include \"base.c\"\n";
        out << "\n";
        if(!listChangedParts.isEmpty())
            out << "#define LEN" + strFrameCount + " " + strSize + "\n";
        out << "void run" + strFrameCount + "() {\n";
        if(!listChangedParts.isEmpty()) {
            FN_FORMATCP("X", cp.x / xSum);
            FN_FORMATCP("Y", cp.y / ySum);
            FN_FORMATCP("Len", cp.len);
            out << "    runSingle(arrCpX, arrCpY, arrCpLen, LEN" + strFrameCount + ");\n";
        }
        out << "}\n";
        out << "\n";
        out << "#endif";
        file.close();

        frameCount++;
        index += indexSum;
    }

    //写入run.c
    QFile runFile(canonicalPath + "/run.c");
    if(!runFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        QMessageBox::critical(this, "错误", "写入 \"run.c\" 失败，处理终止");
        return;
    }
    QTextStream out(&runFile);
    for(int i = 0; i < frameCount; i++)
        out << "#include \"" + QString::number(i) + ".c\"\n";
    out << "\n";
    out << "void run() {\n";
    for(int i = 0; i < frameCount; i++)
        out << "    run" + QString::number(i) + "();\n";
    out << "}";
    runFile.close();
}
#undef FN_FORMATCP
